package com.daffodil.flowable.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.daffodil.core.dao.JdbcDao;
import com.daffodil.core.entity.Page;
import com.daffodil.core.entity.Query;
import com.daffodil.core.exception.BaseException;
import com.daffodil.flowable.constant.FlowableConstant;
import com.daffodil.flowable.entity.ActCnHistoricActivity;
import com.daffodil.flowable.entity.ActCnHistoricProcess;
import com.daffodil.flowable.entity.ActFlowSubmitAttachment;
import com.daffodil.flowable.entity.ActFlowSubmitParam;
import com.daffodil.flowable.service.IFlowableHandleService;
import com.daffodil.flowable.util.FlowableUtils;
import com.daffodil.system.entity.SysUser;
import com.daffodil.system.util.LoginUserUtils;
import com.daffodil.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 通用流程办理实现
 * 
 * @author yweijian
 * @date 2020年1月16日
 * @version 1.0
 */
@Slf4j
@Service
public class FlowableHandleServiceImpl implements IFlowableHandleService {

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private JdbcDao jdbcDao;
    
    /**
     * 流程环节执行人是否可作废，默认发起人可作废，流程执行环节节点不可作废操作
     */
    @Value("${flowable.button.trash:false}")
    private boolean FLOWABLE_BUTTON_TRASH;

    @Override
    @Transactional
    public void generalDirectHandle(ActFlowSubmitParam param) {
        
        // 获取当前任务
        Task task = FlowableUtils.getCurrentTask(param.getBusinessKey(), param.getUserId());
        
        // 保存办理意见表
        processEngine.getIdentityService().setAuthenticatedUserId(param.getUserId());
        if(StringUtils.isNotEmpty(param.getMessage())) {
            processEngine.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), param.getMessage().trim());
        }

        // 保存办理附件
        if(StringUtils.isNotEmpty(param.getAttachments())) {
            for(ActFlowSubmitAttachment attachment : param.getAttachments()) {
                if(StringUtils.isNotEmpty(attachment.getUrl())) {
                    processEngine.getTaskService()
                    .createAttachment(attachment.getType(), task.getId(), task.getProcessInstanceId(), attachment.getName(), attachment.getDesc(), attachment.getUrl());
                }
            }
        }

        // 更新办理参数
        Map<String, Object> variables = new HashMap<String, Object>();
        
        Map<String, Object> processVariables = task.getProcessVariables();//旧参数
        if(StringUtils.isNotNull(processVariables)){
            variables.putAll(processVariables);
        }
        
        Map<String, Object> handleVariables = FlowableUtils.getNextHandleVariables(param.getBusinessKey(), param.getUserId(), param.getHandleId());//新参数
        if(StringUtils.isNotNull(handleVariables)){
            variables.putAll(handleVariables);
        }
        
        // 保存结束任务，并会新开启下一个任务
        processEngine.getTaskService().complete(task.getId(), variables);
    }

    @Override
    @Transactional
    public void changeTaskAssignee(String businessKey, String nodeId, String userId) {
        Task task = processEngine.getTaskService().createTaskQuery().processInstanceBusinessKey(businessKey)
                .taskDefinitionKey(nodeId).active().singleResult();
        // 设置下一步任务操作人员,若任务不存在即该流程任务结束了
        if(StringUtils.isNotNull(task) && task.getTaskDefinitionKey().equals(nodeId)) {
            processEngine.getTaskService().setAssignee(task.getId(), userId); 
        }
    }

    @Override
    @Transactional
    public void deleteProcessInstance(String businessKey) {
        ProcessInstance instance = processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
        if(null != instance){
            processEngine.getRuntimeService().deleteProcessInstance(instance.getId(), "作废");
        }
    }

    @Override
    @Transactional
    public void backProcessInstance(String businessKey, String userId) {
        //撤办任务一定是当前这个用户最近的一次完成的任务
        try {
            HistoricTaskInstance taskInstance = processEngine.getHistoryService().createHistoricTaskInstanceQuery().processInstanceBusinessKey(businessKey)
                    .taskAssignee(userId).orderByHistoricTaskInstanceEndTime().desc().list().iterator().next();

            this.moveExecutionsToSingleActivityId(businessKey, taskInstance.getProcessInstanceId(), taskInstance.getTaskDefinitionKey(), userId, taskInstance.getAssignee(), "撤办");
        }catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BaseException(60005);
        }
    }

    @Override
    @Transactional
    public void rejectProcessInstance(String businessKey, String message, String userId) {
        try {
            ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
                    .processInstanceBusinessKey(businessKey).active().singleResult();

            //驳回任务一定是当前流程发起人第一次完成的任务节点
            HistoricTaskInstance taskInstance = processEngine.getHistoryService().createHistoricTaskInstanceQuery().processInstanceBusinessKey(businessKey)
                    .taskAssignee(processInstance.getStartUserId()).orderByHistoricTaskInstanceEndTime().asc().list().iterator().next();

            message = StringUtils.isNotEmpty(message) ? ("驳回，" + message) : "驳回";

            this.moveExecutionsToSingleActivityId(businessKey, taskInstance.getProcessInstanceId(), taskInstance.getTaskDefinitionKey(), userId, processInstance.getStartUserId(), message);
        }catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BaseException(60006);
        }
    }

    @Override
    public List<ActCnHistoricProcess> selectUnfinishedProcessList(Query<ActCnHistoricProcess> query, SysUser user) {
        return this.selectHistoricProcessList(query, user, false);
    }

    @Override
    public List<ActCnHistoricProcess> selectFinishedProcessList(Query<ActCnHistoricProcess> query, SysUser user) {
        return this.selectHistoricProcessList(query, user, true);
    }

    @Override
    public List<ActCnHistoricProcess> selectCompletedProcessList(Query<ActCnHistoricProcess> query) {
        List<ActCnHistoricProcess> datas = new ArrayList<ActCnHistoricProcess>();
        List<HistoricProcessInstance> list = null;
        HistoricProcessInstanceQuery processQuery = processEngine.getHistoryService().createHistoricProcessInstanceQuery();
        if (StringUtils.isNotEmpty(query.getEntity().getName())) {
            processQuery.processInstanceNameLike("%" + query.getEntity().getName() + "%");
        }
        if (StringUtils.isNotNull(query.getPage())) {
            Page page = query.getPage();
            page.setTotalRow((int) processQuery.count());
            list = processQuery.finished().orderByProcessInstanceStartTime().desc().listPage(page.getFromIndex(), page.getPageSize());
        } else {
            list = processQuery.finished().orderByProcessInstanceStartTime().desc().list();
        }
        if (StringUtils.isNotEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                HistoricProcessInstance hpi = list.get(i);
                ActCnHistoricProcess hp = new ActCnHistoricProcess(hpi);
                datas.add(hp);
            }
        }
        return datas;
    }

    @Override
    public List<ActCnHistoricProcess> selectMyselfProcessList(Query<ActCnHistoricProcess> query, SysUser user, boolean taskIsFinished, boolean processIsfinished) {
        if(StringUtils.isNull(user)) {
            return null;
        }
        ActCnHistoricProcess process = query.getEntity() != null ? query.getEntity() : new ActCnHistoricProcess();
        List<HistoricTaskInstance> tasks = null;
        List<ActCnHistoricProcess> datas = new ArrayList<ActCnHistoricProcess>();
        List<HistoricProcessInstance> list = null;
        Set<String> processInstanceIds = new HashSet<String>();
        HistoricTaskInstanceQuery taskQuery = processEngine.getHistoryService().createHistoricTaskInstanceQuery();
        HistoricProcessInstanceQuery processQuery = processEngine.getHistoryService().createHistoricProcessInstanceQuery();
        
        if(taskIsFinished) {
            taskQuery.finished();//任务完成，已办
        }else {
            taskQuery.unfinished().taskAssignee(user.getLoginName());//任务未完成，待办且是自己的任务
        }
        
        if(processIsfinished) {
            taskQuery.processFinished();//流程结束
        }else {
            taskQuery.processUnfinished();//流程未结束
        }
        
        if (StringUtils.isNotNull(query.getPage())) {
            Page page = query.getPage();
            tasks = taskQuery.orderByHistoricTaskInstanceStartTime().desc()
                    .listPage(page.getFromIndex(), Double.valueOf(Math.ceil(page.getPageSize() * 10 * 0.75)).intValue());
        }else {
            tasks = taskQuery.list();
        }

        if (StringUtils.isNotEmpty(tasks)) {
            for (int i = 0; i < tasks.size(); i++) {
                HistoricTaskInstance task = tasks.get(i);
                processInstanceIds.add(task.getProcessInstanceId());
            }
        }
        
        // 从指定的未完成任务且流程未结束的流程实例ID中查询，如果没有则结束查询
        if (StringUtils.isNotEmpty(processInstanceIds)) {
            processQuery.processInstanceIds(processInstanceIds);
        } else {
            return datas;
        }
        if (StringUtils.isNotEmpty(process.getName())) {
            processQuery.processInstanceNameLike("%" + process.getName() + "%");
        }
        if(StringUtils.isNotNull(process.getStartTime())) {
            processQuery.startedAfter(process.getStartTime());
        }
        if(StringUtils.isNotNull(process.getEndTime())) {
            processQuery.startedBefore(process.getEndTime());
        }
        
        //只查询自己发起的流程
        processQuery.startedBy(user.getLoginName());
        
        if (StringUtils.isNotNull(query.getPage())) {
            Page page = query.getPage();
            page.setTotalRow((int) processQuery.count());
            list = processQuery.orderByProcessInstanceStartTime().desc().listPage(page.getFromIndex(), page.getPageSize());
        } else {
            list = processQuery.orderByProcessInstanceStartTime().desc().list();
        }
        if (StringUtils.isNotEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                HistoricProcessInstance hpi = list.get(i);
                ActCnHistoricProcess hp = new ActCnHistoricProcess(hpi);
                datas.add(hp);
            }
        }
        return datas;
    }

    /**
     * 查询历史流程任务
     * @param query
     * @param taskIsFinished
     * @return
     */
    private List<ActCnHistoricProcess> selectHistoricProcessList(Query<ActCnHistoricProcess> query, SysUser user, boolean taskIsFinished) {
        if(StringUtils.isNull(user)) {
            return null;
        }
        ActCnHistoricProcess process = query.getEntity() != null ? query.getEntity() : new ActCnHistoricProcess();
        List<HistoricTaskInstance> tasks = null;
        List<ActCnHistoricProcess> datas = new ArrayList<ActCnHistoricProcess>();
        List<HistoricProcessInstance> list = null;
        Set<String> processInstanceIds = new HashSet<String>();
        HistoricTaskInstanceQuery taskQuery = processEngine.getHistoryService().createHistoricTaskInstanceQuery();
        HistoricProcessInstanceQuery processQuery = processEngine.getHistoryService().createHistoricProcessInstanceQuery();
        
        if(!LoginUserUtils.isAdmin(user)) {//超级管理员可以查看所有
            taskQuery.taskAssignee(user.getLoginName());
        }
        
        if(taskIsFinished) {
            taskQuery.finished();//已办任务
        }else {
            taskQuery.unfinished().processUnfinished();//待办任务
        }
        
        if (StringUtils.isNotNull(query.getPage())) {
            Page page = query.getPage();
            tasks = taskQuery.orderByHistoricTaskInstanceStartTime().desc()
                    .listPage(page.getFromIndex(), Double.valueOf(Math.ceil(page.getPageSize() * 10 * 0.75)).intValue());
        }else {
            tasks = taskQuery.list();
        }

        // 排除自己发起，自己提交的任务流程
        if (StringUtils.isNotEmpty(tasks)) {
            for (int i = 0; i < tasks.size(); i++) {
                HistoricTaskInstance task = tasks.get(i);
                HistoricProcessInstance instance = processEngine.getHistoryService()
                        .createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId())
                        .singleResult();
                if (null != instance && instance.getStartUserId().equals(user.getLoginName())) {
                    continue;
                }
                processInstanceIds.add(task.getProcessInstanceId());
            }
        }
        
        // 从指定的未完成任务且流程未结束的流程实例ID中查询，如果没有则结束查询
        if (StringUtils.isNotEmpty(processInstanceIds)) {
            processQuery.processInstanceIds(processInstanceIds);
        } else {
            return datas;
        }
        if (StringUtils.isNotEmpty(query.getEntity().getName())) {
            processQuery.processInstanceNameLike("%" + query.getEntity().getName() + "%");
        }
        if(StringUtils.isNotNull(process.getStartTime())) {
            processQuery.startedAfter(process.getStartTime());
        }
        if(StringUtils.isNotNull(process.getEndTime())) {
            processQuery.startedBefore(process.getEndTime());
        }
        if (StringUtils.isNotNull(query.getPage())) {
            Page page = query.getPage();
            page.setTotalRow(Long.valueOf(processQuery.count()).intValue());
            list = processQuery.orderByProcessInstanceStartTime().desc().listPage(page.getFromIndex(), page.getPageSize());
        } else {
            list = processQuery.orderByProcessInstanceStartTime().desc().list();
        }
        if (StringUtils.isNotEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                HistoricProcessInstance hpi = list.get(i);
                ActCnHistoricProcess hp = new ActCnHistoricProcess(hpi);
                datas.add(hp);
            }
        }

        return datas;
    }

    @Override
    public List<ActCnHistoricActivity> selectHistoricActivityByBusinessKey(String businessKey) {
        List<ActCnHistoricActivity> datas = new ArrayList<ActCnHistoricActivity>();
        HistoricProcessInstance instance = processEngine.getHistoryService().createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey).singleResult();
        if (null == instance) {
            return datas;
        }
        Set<String> activityTypes = new HashSet<String>();
        activityTypes.add(BpmnXMLConstants.ELEMENT_EVENT_START);
        activityTypes.add(BpmnXMLConstants.ELEMENT_TASK_USER);
        activityTypes.add(BpmnXMLConstants.ELEMENT_EVENT_END);
        List<HistoricActivityInstance> list = processEngine.getHistoryService().createHistoricActivityInstanceQuery()
                .processInstanceId(instance.getId())
                .activityTypes(activityTypes)
                .orderByHistoricActivityInstanceStartTime().asc().list();
        if (StringUtils.isNotEmpty(list)) {
            for (int i = 0; i < list.size(); i++) {
                HistoricActivityInstance hai = list.get(i);
                ListIterator<Comment> it = processEngine.getTaskService().getTaskComments(hai.getTaskId()).listIterator();
                ActCnHistoricActivity ha = new ActCnHistoricActivity(hai, it);
                datas.add(ha);
            }
        }
        return datas;
    }

    /**
     * 执行跳转
     * @param businessKey 业务ID
     * @param processInstanceId 流程实例ID
     * @param targetActivityId 跳转目标节点
     * @param currentUserId 当前操作用户
     * @param originalUserId 原始操作用户
     * @param message 内容消息（操作原因）
     */
    private void moveExecutionsToSingleActivityId(String businessKey, String processInstanceId, String targetActivityId, String currentUserId, String originalUserId, String message) {

        List<Execution> executions = processEngine.getRuntimeService().createExecutionQuery().processInstanceId(processInstanceId).parentId(processInstanceId).list();

        //执行跳转，是当前正在执行的节点到目标节点
        List<String> moveExecutionIds = new ArrayList<String>();
        for(Execution execution : executions) {
            moveExecutionIds.add(execution.getId());
        }
        processEngine.getRuntimeService().createChangeActivityStateBuilder().processInstanceId(processInstanceId)
            .moveExecutionsToSingleActivityId(moveExecutionIds, targetActivityId).changeState();

        //更新当前节点任务删除原因
        for(Execution execution : executions) {
            try {
                HistoricTaskInstance historicTaskInstance = processEngine.getHistoryService().createHistoricTaskInstanceQuery().processInstanceBusinessKey(businessKey)
                        .processInstanceId(execution.getProcessInstanceId())
                        .executionId(execution.getId())
                        .taskDefinitionKey(execution.getActivityId())
                        .orderByTaskCreateTime().desc().list().iterator().next();

                String sql = "update act_hi_actinst set delete_reason_=?,assignee_=? where proc_inst_id_=? and execution_id_=? and act_id_=? and task_id_=?";
                List<Object> paras = new ArrayList<Object>();
                paras.add(message);
                paras.add(currentUserId);//操作人
                paras.add(execution.getProcessInstanceId());
                paras.add(execution.getId());
                paras.add(execution.getActivityId());
                paras.add(historicTaskInstance.getId());
                jdbcDao.execute(sql, paras);

            }catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new BaseException(60007);
            }
        }

        //将当前新的节点的任务执行人设置为当前用户
        Task currentTask = processEngine.getTaskService().createTaskQuery()
                .processInstanceBusinessKey(businessKey).processInstanceId(processInstanceId).active().singleResult();
        if("$INITIATOR".equals(currentTask.getAssignee()) || StringUtils.isEmpty(currentTask.getAssignee())) {
            processEngine.getTaskService().setAssignee(currentTask.getId(), originalUserId);
        }
    }

    @Override
    public Map<String, Boolean> buildProcessInstanceButton(String businessKey, SysUser user) {
        Map<String, Boolean> buttons = new HashMap<String, Boolean>();
        buttons.put(FlowableConstant.actFlowClose, true);//默认都拥有【关闭】按钮功能
        buttons.put(FlowableConstant.actFlowSave, false);
        buttons.put(FlowableConstant.actFlowSend, false);
        buttons.put(FlowableConstant.actFlowReject, false);
        buttons.put(FlowableConstant.actFlowBack, false);
        buttons.put(FlowableConstant.actFlowTrash, false);
        
        if(StringUtils.isNull(user)) {
            throw new BaseException(60008);
        }
        
        //流程业务还没创建
        if(StringUtils.isEmpty(businessKey)) {
            buttons.put(FlowableConstant.actFlowSave, true);
            return buttons;
        }
        
        SysUser startUser = FlowableUtils.getStartUser(businessKey);
        FlowElement flowElement = FlowableUtils.getCurrentFlowNode(businessKey, user.getLoginName());
        
        if (FlowableUtils.isUserTaskNode(flowElement)) {
            UserTask userTask = (UserTask) flowElement;
            String assignee = userTask.getAssignee();
            if("$INITIATOR".equals(assignee)) {//执行人是发起人即发起的用户节点
                if(StringUtils.isNotNull(startUser) && user.getId().equals(startUser.getId())) {
                    buttons.put(FlowableConstant.actFlowSave, true);//【保存】按钮功能
                }
            }
        }
        
        List<SysUser> users = FlowableUtils.getCurrentNodeHandleUsers(businessKey, user.getLoginName());
        if(StringUtils.isNotEmpty(users)) {
            for(int i= 0; i < users.size(); i++) {
                if(user.getId().equals(users.get(i).getId())) {
                    buttons.put(FlowableConstant.actFlowSend, true);//包含当前用户则拥有【发送】按钮功能
                    if(FLOWABLE_BUTTON_TRASH) {
                        buttons.put(FlowableConstant.actFlowTrash, true);//环节执行人可【作废】按钮功能
                    }
                    break;
                }
            }
        }
        
        if(!buttons.get(FlowableConstant.actFlowSave) && !buttons.get(FlowableConstant.actFlowSend)) {//不包含【发送】和【保存】功能则可能包含撤办
            if(StringUtils.isNotEmpty(FlowableUtils.getCurrentTask(businessKey))) {//任务结束就不能有【撤办】
                //如果当前节点是在发起人的节点，即任务办理人==发起人的时候则不能撤办
                boolean actFlowBack = true;
                List<Task> tasks = FlowableUtils.getCurrentTask(businessKey);
                if(StringUtils.isNotEmpty(tasks)) {
                    for(Task task : tasks) {
                        if(StringUtils.isNotNull(startUser) && startUser.getLoginName().equals(task.getAssignee())) {
                            actFlowBack = false;
                        }
                    }
                }
                if(actFlowBack) {
                    buttons.put(FlowableConstant.actFlowBack, true);
                }
            }
        }
        if(buttons.get(FlowableConstant.actFlowSend) && !buttons.get(FlowableConstant.actFlowSave)) {//包含【发送】且不包含【保存】功能则可能包含驳回
            if(StringUtils.isNotEmpty(FlowableUtils.getCurrentTask(businessKey))) {//任务结束就不能有【驳回】
                buttons.put(FlowableConstant.actFlowReject, true);//【驳回】按钮功能
            }
        }
        
        if(StringUtils.isNotNull(startUser) && user.getId().equals(startUser.getId())) {
            buttons.put(FlowableConstant.actFlowTrash, true);//当前登录用户是当前流程发起人则，拥有【作废】按钮功能
        }
        
        return buttons;
    }

}